package IO;
/*
Reader是带编码转换器的InputStream，它把byte转换为char，而Writer就是带编码转换器的OutputStream，它把char转换为byte并输出。

Writer和OutputStream的区别如下：

OutputStream	                     Writer
字节流，以byte为单位	                  字符流，以char为单位
写入字节（0~255）：void write(int b)	   写入字符（0~65535）：void write(int c)
写入字节数组：void write(byte[] b)	   写入字符数组：void write(char[] c)
无对应方法	                           写入String：void write(String s)

Writer是所有字符输出流的超类，它提供的方法主要有：

写入一个字符（0~65535）：void write(int c)；
写入字符数组的所有字符：void write(char[] c)；
写入String表示的所有字符：void write(String s)。

【FileWriter】    --->FileReader
FileWriter就是向文件中写入字符流的Writer。它的使用方法和FileReader类似：

try (Writer writer = new FileWriter("readme.txt", StandardCharsets.UTF_8)) {
    writer.write('H'); // 写入单个字符
    writer.write("Hello".toCharArray()); // 写入char[]
    writer.write("Hello"); // 写入String
}

【CharArrayWriter】   ---->CharArrayReader
CharArrayWriter可以在内存中创建一个Writer，它的作用实际上是构造一个缓冲区，可以写入char，最后得到写入的char[]数组，这和ByteArrayOutputStream非常类似：

try (CharArrayWriter writer = new CharArrayWriter()) {
    writer.write(65);
    writer.write(66);
    writer.write(67);
    char[] data = writer.toCharArray(); // { 'A', 'B', 'C' }
}

【StringWriter】     ---->StringReader
StringWriter也是一个基于内存的Writer，它和CharArrayWriter类似。实际上，StringWriter在内部维护了一个StringBuffer，并对外提供了Writer接口。

【OutputStreamWriter】    ---->InputStreamReader
除了CharArrayWriter和StringWriter外，普通的Writer实际上是基于OutputStream构造的，它接收char，然后在内部自动转换成一个或多个byte，并写
入OutputStream。因此，OutputStreamWriter就是一个将任意的OutputStream转换为Writer的转换器：

try (Writer writer = new OutputStreamWriter(new FileOutputStream("readme.txt"), "UTF-8")) {
    // TODO:
}
上述代码实际上就是FileWriter的一种实现方式。这和上一节的InputStreamReader是一样的。

小结
Writer定义了所有字符输出流的超类：

FileWriter实现了文件字符流输出；

CharArrayWriter和StringWriter在内存中模拟一个字符流输出。

使用try (resource)保证Writer正确关闭。

Writer是基于OutputStream构造的，可以通过OutputStreamWriter将OutputStream转换为Writer，转换时需要指定编码。
 */
import java.io.FileWriter;
import java.nio.charset.Charset;

class 使用Writer {
  public static void main(String[] args) {

    String file = "d:\\tmp\\output.txt";
    String data = "This is the data in the output file";
    try {
      //使用默认编码创建一个FileReader
      FileWriter output1 = new FileWriter(file);

      //创建一个FileReader指定编码
      FileWriter output2 = new FileWriter(file, Charset.forName("GBK"));
      
      output1.write(data);
      
      output2.write(data);
      
      //返回阅读器的字符编码
      System.out.println("output1的字符编码: " + output1.getEncoding());
      System.out.println("output2的字符编码: " + output2.getEncoding());

      // 关闭 reader
      output1.close();
      output2.close();
    }

    catch(Exception e) {
      e.getStackTrace();
    }
  }
}
/*
java.io.Reader
所有字符输入流的super class，它最主要的方法是：

read()
public int read() throws IOException;
该方法读取字符流的下个字符；返回字符表示的int值，范围是0~65535，读到末尾则返回-1。

read( char[ ] c )
public int read( char[] c ) throws IOException;
一次读取若干个字符并填充到char [ ]数组；返回实际读入的字符数，最大不超过char[ ]数组长度，返回-1表示结束。

java.io.Writer
所有字符输出流的super class，主要方法有：

--写入单个char（0~65535）：void write( int c )；
--写入char [ ]：void write( char[ ] c )；
--写入String：void write( String s )。
实现类：Reader
1）FileReader：打开一个文件并读取所有字符
流程
1、打开文件并获取Reader；
try( Reader reader = new FileReader( "src/readme.txt" , StandardCharsets.UTF_8) ) {...}
初始化时，第二个参数为文件编码类型，缺省时可以正常打开纯ASCII编码的文本文件；如果文件包含中文，就要指定编码为UTF-8。

2、反复调用read()直到返回-1；
for(;;){
    int n = reader.read();
    if(n==-1) break;
    System.out.println((char)n);
}
2.5、read( char[ ] c )一次读取若干字符并填充到char [ ]数组
先设置一个缓冲区，然后每次尽可能地填充缓冲区

        try(Reader reader = new FileReader("src/readme.txt",StandardCharsets.UTF_8)){
            char [] buffer = new char [1000];
            int n;
            while((n=reader.read(buffer))!=-1)
                System.out.println("read "+n+" chars.");
        }

2）CharArrayReader：在内存中模拟Reader
类似ByteArrayInputStream，其作用实际上是把char[ ]数组变成一个Reader

try(Reader reader = new CharArrayReader("Hello.".toCharArray())){
}
在内存中模拟Reader的意思是：我们不需要从一个文件构造Reader，可以直接将一个String转换为char [ ]，在将char [ ]传入并构造Reader，起到模拟从文件读取这个char[ ]的作用。

3）StringReader：类似CharArrayReader，但可以直接以String作为数据源
try( Reader reader = new StringReader("Hello")){ ... }
4）InputStreamReader
Reader本质上是一个基于InputStream的从byte到char的转换器。

InputStreamReader是一个可以把任何InputStream转换为Reader的转换器：

//InputStream
InputStream input = new FileInputStream("src/readme.txt");

//由InputStream构造Reader
Reader reader = new InputStreamReader( input , "UTF-8");
构造InputStreamReader时，我们需要传入InputStream和编码，从而得到一个Reader对象。

上述代码可以用try( resource )更简洁地改写如下：

try(Reader reader = new InputStreamReader( new FileInputStream( "src/readme.txt") , "UTF-8" )){ ... }
上述代码就是FileReader的一种实现形式。

实现类：Writer
1）FileWriter：写入文件的Writer
try(Writer writer = new FileWriter("readme.txt",StandardCharsets.UTF_8)){
    writer.write('H');
    writer.write("Hello".toCharArray());
    writer.write("Hello");
}
2）CharArrayWriter：在内存中模拟Writer
在内存中模拟Writer的意思是：构造一个buffer，向其中写入char，最后得到写入的char [ ]数组（而不用输出为真正的文件），这和ByteArrayOutputStream类似。

在内存中保存Writer写入的char[ ]（内存中），并不保存为真正的文件（硬盘中），这就是“模拟”的意思。


try(CharArrayWriter writer = new CharArrayWriter()){
    writer.write(65);
    writer.write(66);
    writer.write(67);
    char [] data = writer.toCharArray();
}

3）StringWriter
类似于CharArrayWriter，只不过最终在内存中维护的是StringBuffer而不是char [ ]，并对外提供了Writer接口。

4）OutputStreamWriter
和Reader类似，Writer实际上是基于OutputStream构造的——它接收char，在内部转换为一个或多个byte，并写入OutputStream。因此，OutputStreamWriter就是一个将任意OutputStream转换为Writer的转换器：

try(Writer writer = new OutputStreamWriter( new FileOutputStream("readme.txt"),"UTF-8" ) ){
    ...
}

5）Files.newBufferedWriter
	Files.newBufferedWriter(Paths.get("C:\\my2.ini"), StandardCharsets.UTF_8);
	BufferedWriter writer = Files.newBufferedWriter(Paths.get("C:\\my2.ini"), StandardCharsets.UTF_8);
   writer.write("测试文件写操作");
   writer.flush()
*/